////////////////////////////////////////////////////////////////////////////
//
//  Crytek Engine Source File.
//  Copyright (C), Crytek Studios, 2002.
// -------------------------------------------------------------------------
//  File name:   trackviewundo.cpp
//  Version:     v1.00
//  Created:     30/8/2002 by Timur.
//  Compilers:   Visual Studio.NET
//  Description: 
// -------------------------------------------------------------------------
//  History:
//
////////////////////////////////////////////////////////////////////////////

#include "StdAfx.h"
#include "TrackViewUndo.h"
#include "Objects/SequenceObject.h"
#include "Objects/EntityObject.h"
#include <IMovieSystem.h>

//////////////////////////////////////////////////////////////////////////
CAbstractUndoTrackObject::CAbstractUndoTrackObject( IAnimSequence *pSequence )
{
	assert( pSequence != NULL );

	m_sequenceName = pSequence->GetFullName();
}

CAbstractUndoTrackObject::TTrackPath CAbstractUndoTrackObject::SaveTrackPath( IAnimTrack *pTrack )
{
	TTrackPath path;

	IAnimSequence *pSequence = gEnv->pMovieSystem->FindSequence(m_sequenceName.GetBuffer());
	assert( pSequence != NULL );

	// Search for track in sequence
	int sequenceNodeCount = pSequence->GetNodeCount();
	bool found = false;

	for (int nodeIndex = 0; nodeIndex < sequenceNodeCount && !found; ++nodeIndex)
	{
		IAnimNode *pCurrentNode = pSequence->GetNode( nodeIndex );
		path.push_back( nodeIndex );

		int nodeTrackCount = pCurrentNode->GetTrackCount();
		for (int trackIndex = 0; trackIndex < nodeTrackCount && !found; ++trackIndex)
		{
			IAnimTrack *pCurrentTrack = pCurrentNode->GetTrackByIndex( trackIndex );
			
			if(pCurrentTrack == pTrack)
			{
					path.push_back( trackIndex );
					found = true;
			}
			else
			{
				int numSubTracks = pCurrentTrack->GetSubTrackCount();
				for (int subTrackIndex = 0; subTrackIndex < numSubTracks; ++subTrackIndex)
				{
					IAnimTrack *pSubTrack = pCurrentTrack->GetSubTrack(subTrackIndex);
					if(pSubTrack == pTrack)
					{
						path.push_back( trackIndex );
						path.push_back( subTrackIndex );
						found = true;
					}
				}
			}
		}

		if (!found)
			path.pop_back();
	}

	// Make sure a path was found
	assert( found && (path.size() == 2 || path.size() == 3) );

	return path;
}

//////////////////////////////////////////////////////////////////////////
IAnimTrack *CAbstractUndoTrackObject::GetTrackFromSavedPath( TTrackPath &path )
{
	IAnimSequence *pSequence = gEnv->pMovieSystem->FindSequence(m_sequenceName.GetBuffer());
	assert( pSequence != NULL );

	IAnimNode *pAnimNode = pSequence->GetNode( path[0] );
	IAnimTrack *pTrack = pAnimNode->GetTrackByIndex( path[1] );

	if( path.size() == 3 )	
		pTrack = pTrack->GetSubTrack( path[2] );			

	assert( pTrack != NULL );
	return pTrack;
}

//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
CUndoTrackObject::CUndoTrackObject( IAnimTrack *pTrack, IAnimSequence* pSequence )
	: CAbstractUndoTrackObject(pSequence)
{
	// Stores the current state of this track.
	assert( pTrack != 0 );

	m_pathToTrack = SaveTrackPath( pTrack );	

	// Store undo info.
	m_undo = XmlHelpers::CreateXmlNode("Undo");
	pTrack->Serialize( m_undo,false );

	CSequenceObject* pObj = static_cast<CSequenceObject*>(pSequence->GetOwner());
	if(pObj)
		pObj->SetLayerModified();
}

//////////////////////////////////////////////////////////////////////////
void CUndoTrackObject::Undo( bool bUndo )
{
	if (!m_undo)
		return;

	IAnimSequence *pSequence = gEnv->pMovieSystem->FindSequence(m_sequenceName.GetBuffer());
	assert( pSequence != NULL );

	IAnimTrack *pTrack = GetTrackFromSavedPath( m_pathToTrack );

	if (bUndo)
	{ 
		m_redo = XmlHelpers::CreateXmlNode("Redo");
		pTrack->Serialize( m_redo,false );
	}
	// Undo track state.
	pTrack->Serialize( m_undo,true );
	
	CSequenceObject* pObj = static_cast<CSequenceObject*>(pSequence->GetOwner());
	if(pObj)
		pObj->SetLayerModified();	

	if (bUndo)
	{
		// Refresh stuff after undo.
		GetIEditor()->GetAnimation()->ForceAnimation();
		GetIEditor()->UpdateTrackView(true);
	}
}

//////////////////////////////////////////////////////////////////////////
void CUndoTrackObject::Redo()
{
	if (!m_redo)
		return;

	IAnimSequence *pSequence = gEnv->pMovieSystem->FindSequence(m_sequenceName.GetBuffer());
	assert( pSequence != NULL );

	IAnimTrack *pTrack = GetTrackFromSavedPath( m_pathToTrack );

	// Redo track state.
	pTrack->Serialize( m_redo,true );

	CSequenceObject* pObj = static_cast<CSequenceObject*>(pSequence->GetOwner());
	if(pObj)
		pObj->SetLayerModified();

	// Refresh stuff after undo.
	GetIEditor()->GetAnimation()->ForceAnimation();
	GetIEditor()->UpdateTrackView(true);
}

//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////
CUndoAnimSequenceGroupAdd::CUndoAnimSequenceGroupAdd( IAnimSequenceGroup *pGroup )
{
	assert(pGroup);

	m_groupName = pGroup->GetFullName();

	m_undo = XmlHelpers::CreateXmlNode("Undo");
	pGroup->Serialize(m_undo, false);
}

//////////////////////////////////////////////////////////////////////////
void CUndoAnimSequenceGroupAdd::Undo( bool bUndo )
{
	IAnimSequenceGroup *pGroup = GetIEditor()->GetMovieSystem()->FindSequenceGroup(m_groupName.GetBuffer());
	assert(pGroup);

	if (!m_undo)
		return;

	if (bUndo)
	{
		m_redo = XmlHelpers::CreateXmlNode("Redo");
		pGroup->Serialize(m_redo, false);
	}

	pGroup->Serialize(m_undo, true);

	if (bUndo)
	{
		// Refresh stuff after undo.
		GetIEditor()->GetAnimation()->ForceAnimation();
		GetIEditor()->UpdateTrackView(false);
	}
}

//////////////////////////////////////////////////////////////////////////
void CUndoAnimSequenceGroupAdd::Redo()
{
	IAnimSequenceGroup *pGroup = GetIEditor()->GetMovieSystem()->FindSequenceGroup(m_groupName.GetBuffer());
	assert(pGroup);

	if (!m_redo)
		return;

	// Redo sequence state.
	pGroup->Serialize(m_redo, true);

	// Refresh stuff after undo.
	GetIEditor()->GetAnimation()->ForceAnimation();
	GetIEditor()->UpdateTrackView(false);
}

//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////
CUndoAnimSequenceGroupDelete::CUndoAnimSequenceGroupDelete( IAnimSequenceGroup *pGroup )
{
	assert(pGroup);
	assert(pGroup->GetChildGroupsCount() == 0 && pGroup->GetChildSequencesCount() == 0);

	m_pGroup = pGroup;
	m_undo = XmlHelpers::CreateXmlNode("Undo");
	m_pGroup->Serialize(m_undo, false);
}

//////////////////////////////////////////////////////////////////////////
void CUndoAnimSequenceGroupDelete::Undo( bool bUndo )
{
	IAnimSequenceGroup *pParent = m_pGroup->GetParent();
	m_pGroup->Serialize(m_undo, true);
	pParent->AddChildGroup(m_pGroup);

	if (bUndo)
	{
		// Refresh stuff after undo.
		GetIEditor()->GetAnimation()->ForceAnimation();
		GetIEditor()->UpdateTrackView(false);
	}
}

//////////////////////////////////////////////////////////////////////////
void CUndoAnimSequenceGroupDelete::Redo()
{
	gEnv->pMovieSystem->DeleteSequenceGroup(m_pGroup);

	// Refresh stuff after redo.
	GetIEditor()->GetAnimation()->ForceAnimation();
	GetIEditor()->UpdateTrackView(false);
}

//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////

static void RecursiveSetLayerModified(IAnimSequenceGroup *pGroup)
{
	for(uint32 i=0; i<pGroup->GetChildSequencesCount(); ++i)
		static_cast<CSequenceObject*>(pGroup->GetChildSequence(i)->GetOwner())->SetLayerModified();

	for(uint32 i=0; i<pGroup->GetChildGroupsCount(); ++i)
		RecursiveSetLayerModified(pGroup->GetChildGroup(i));
}

//////////////////////////////////////////////////////////////////////////
CUndoAnimSequenceGroupRename::CUndoAnimSequenceGroupRename( IAnimSequenceGroup *pGroup )
{
	assert(pGroup);

	m_pGroup = pGroup;
	m_undoName = m_pGroup->GetName();

	RecursiveSetLayerModified(m_pGroup);
}

//////////////////////////////////////////////////////////////////////////
void CUndoAnimSequenceGroupRename::Undo( bool bUndo )
{
	if (bUndo)
	{
		m_redoName = m_pGroup->GetName();
	}

	m_pGroup->SetName(m_undoName.GetBuffer());

	if (bUndo)
	{
		// Refresh stuff after undo.
		GetIEditor()->GetAnimation()->ForceAnimation();
		GetIEditor()->UpdateTrackView(false);
	}
}

//////////////////////////////////////////////////////////////////////////
void CUndoAnimSequenceGroupRename::Redo()
{
	m_pGroup->SetName(m_redoName.GetBuffer());

	// Refresh stuff after redo.
	GetIEditor()->GetAnimation()->ForceAnimation();
	GetIEditor()->UpdateTrackView(false);
}

//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////
CUndoAnimSequenceGroupReparent::CUndoAnimSequenceGroupReparent( IAnimSequenceGroup *pGroup )
{
	assert(pGroup);

	m_pGroup = pGroup;
	m_undoParent = m_pGroup->GetParent();

	RecursiveSetLayerModified(m_pGroup);
}

//////////////////////////////////////////////////////////////////////////
void CUndoAnimSequenceGroupReparent::Undo( bool bUndo )
{
	if (bUndo)
	{
		m_redoParent = m_pGroup->GetParent();
	}

	m_pGroup->SetParent(m_undoParent);

	if (bUndo)
	{
		// Refresh stuff after undo.
		GetIEditor()->GetAnimation()->ForceAnimation();
		GetIEditor()->UpdateTrackView(false);
	}
}

//////////////////////////////////////////////////////////////////////////
void CUndoAnimSequenceGroupReparent::Redo()
{
	m_pGroup->SetParent(m_redoParent);

	// Refresh stuff after redo.
	GetIEditor()->GetAnimation()->ForceAnimation();
	GetIEditor()->UpdateTrackView(false);
}

//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////
CUndoAnimSequenceObject::CUndoAnimSequenceObject( IAnimSequence *seq )
{
	// Stores the current state of this sequence.
	assert(seq);

	m_sequenceName = seq->GetFullName();
	m_pSequence = seq;

	// Store undo info.
	m_undo = XmlHelpers::CreateXmlNode("Undo");
	m_pSequence->Serialize( m_undo,false );

	static_cast<CSequenceObject*>(m_pSequence->GetOwner())->SetLayerModified();
}

//////////////////////////////////////////////////////////////////////////
void CUndoAnimSequenceObject::Undo( bool bUndo )
{
	IAnimSequence *pSequence = gEnv->pMovieSystem->FindSequence(m_sequenceName.GetBuffer());
	if (pSequence && pSequence != m_pSequence)
		m_pSequence = pSequence;

	if (!m_undo)
		return;

	if (bUndo)
	{
		m_redo = XmlHelpers::CreateXmlNode("Redo");
		m_pSequence->Serialize( m_redo,false );
	}

	// Need to unset sequence from animation context before doing loading
	// to unbind current sequence from objects
	GetIEditor()->GetAnimation()->SetSequence( NULL );

	// Undo sequence state.
	m_pSequence->Serialize( m_undo,true );

	if (m_pSequence->GetFlags() & IAnimSequence::LIGHT_ANIMATION_SET)
		ReloadLightEntities();

	if (bUndo)
	{
		// Refresh stuff after undo.
		GetIEditor()->GetAnimation()->ForceAnimation();
		GetIEditor()->UpdateTrackView(false);
	}
}

//////////////////////////////////////////////////////////////////////////
void CUndoAnimSequenceObject::Redo()
{
	IAnimSequence *pSequence = gEnv->pMovieSystem->FindSequence(m_sequenceName.GetBuffer());
	if (pSequence && pSequence != m_pSequence)
		m_pSequence = pSequence;

	if (!m_redo)
		return;

	// Need to unset sequence from animation context before doing loading
	// to unbind current sequence from objects
	GetIEditor()->GetAnimation()->SetSequence( NULL );

	// Redo sequence state.	
	m_pSequence->Serialize( m_redo,true );

	if (m_pSequence->GetFlags() & IAnimSequence::LIGHT_ANIMATION_SET)
		ReloadLightEntities();

	// Refresh stuff after undo.
	GetIEditor()->GetAnimation()->ForceAnimation();
	GetIEditor()->UpdateTrackView(false);
}

//////////////////////////////////////////////////////////////////////////
// If the light animation set is recreated (by a serialization loading),
// light entities which refer to light animations in it should be reloaded
// to update their pointers properly.
void CUndoAnimSequenceObject::ReloadLightEntities()
{
	std::vector<CBaseObject*> entityObjects;
	GetIEditor()->GetObjectManager()->FindObjectsOfType(RUNTIME_CLASS(CEntityObject), entityObjects);
	for (size_t i=0; i < entityObjects.size(); ++i)
	{
		CEntityObject *pEntityObject = static_cast<CEntityObject*>(entityObjects[i]);
		if (pEntityObject->GetLightAnimation().GetLength() > 0)
		{
			CEntityScript *pScript = pEntityObject->GetScript();
			if (pScript)
				pScript->Reload();
			pEntityObject->Reload(true);
		}
	}
}
